package com.commutersconnect.rest {
  import com.commutersconnect.model._

  import net.liftweb._
  import util._
  import common._
  import mapper._
  import http._
  import rest._
  import json._
  import scala.xml._

  /**
   * CCRestServices provides all of the stateless data from the system.  Any
   * call which does not require you to be logged in should be serviced by this
   * class.
   *
   * At the moment, that is just the following:
   *   *  A list of all reports available in the system
   *     *  Available at /reports/near/lat/long
   *   *  The image associated with a report
   *     *  Full size at /report/id/image
   *     *  Thumbnail at /report/id/thumbnail
   *     *  Additional images available by appending /# to address.
   *     *  If a report does not have an image, you will get back a generic
   *        image.
   */
  object CCRestServices extends RestHelper {

    //In order to return the JSON for a Report, we need the code to make that
    //json.  So, we use this.
    def asJValue(in: List[Report]):JValue = {
      import net.liftweb.json.JsonDSL._

      //We are keeping all of the necessary conversion logic for JSON values
      //above the DSL code for now.  This keeps that section very descriptive
      //of what is wanted, without cluttering it with what is needed to make
      //that.  (Of course, for now this is just one conversion spared.)
      implicit def statusToString(in:ReportStatus.Value):JValue = in.toString

      ("results" -> in.map(report=>
        ("id"->report.id.is) ~
        ("subject"->report.subject.is) ~
        ("status"->report.status.is) ~
        ("text"->report.text.is) ~
        ("latitude"->report.latitude.is) ~
        ("longitude"->report.longitude.is)
      ))
    }

    //Now, we simply declare that we serve requests that look like
    //  /reports/inBounds/northEast/southWest
    //where northEast and southWest are "lat,lng" pairs.
    serve {
      case "reports" :: "inBounds" :: northEast :: southWest :: Nil JsonGet _ => {
        //We have the front end set to give us the bounds as comma separated
        //latLng pairs.
        implicit def latLng(in:String) = {
          val split = in.split(",");
          (split(0), split(1))
        }
        asJValue(Report.findAll(
          BySql[Report]("latitude between ? and ? AND longitude between ? and ?",
          IHaveValidatedThisSQL("jberry", "2012-4-2"),
          southWest._1, northEast._1, southWest._2, northEast._2)))
      }
    }

    //We also service requests for images in this way.
    //  /report/id/image
    //If we can not find the image, we will serve up a sample image.
    //If we find multiple images, return the first.  TODO worry about multiple
    //images.
    serve {
      case "report" :: id :: "image" :: Nil Get _ => {
        import com.commutersconnect.util.ImageScaler
        imageStream(Report.findByKey(id.toLong), 0, ImageScaler.resized(_))
      }
    }

    //If you ask for the thumbnailed version, we do the same as above, but go
    //ahead and resize smaller to save some bandwidth.
    serve {
      case "report" :: id :: "thumbnail" :: Nil Get _ => {
        import com.commutersconnect.util.ImageScaler
        imageStream(Report.findByKey(id.toLong), 0, ImageScaler.thumbnailed(_))
      }
    }

    //We have the common logic for the above image servicing methods here.
    private[this]
    def imageStream(report:Option[Report], index:Int, scaler:(Array[Byte] => Array[Byte])):Option[StreamingResponse] = {
      report.flatMap(report=> {
          var imageBytes = report.images.head.data
          if (imageBytes != null) {
            import com.commutersconnect.util.ImageScaler
            val outBytes = scaler(imageBytes)
            Some(StreamingResponse(
              new java.io.ByteArrayInputStream(outBytes),
              () => (),
              outBytes.length,
              ("Content-Type" -> "image/jpeg") :: Nil,
              Nil,
              200
            ))
        } else {
          None
        }
      })
    }

    //Finally, we have a method that the Boot class can use to initialize all
    //of the service methods in here.
    def init() = {
      LiftRules.statelessDispatchTable.append(CCRestServices)
    }

  }
}

//Placing this under the util package, but keeping in this file since this is
//the only place this is used.
package com.commutersconnect.util {
  object ImageScaler {

    //We aren't too fancy here, just take the bytes, load them as an image,
    //and then write them out as a jpeg at the smaller size.  We do require
    //the desired width, and we calculate the height such that the original
    //image keeps the aspect ration it currently has.
    def resized(in: Array[Byte], width: Int = 400) = {
      import javax.imageio.ImageIO
      import java.awt.image.BufferedImage
      import java.io.ByteArrayOutputStream
      import java.io.ByteArrayInputStream
      import java.awt.RenderingHints

      val byteStream = new ByteArrayInputStream(in)
      val img = ImageIO.read(byteStream);
      val height = width * img.getHeight / img.getWidth
      val scaledImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
      var graphics = scaledImg.createGraphics
      graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
      graphics.drawImage(img, 0, 0, width, height, null)
      val outStream = new ByteArrayOutputStream()
      ImageIO.write(scaledImg, "jpeg", outStream)
      outStream.toByteArray()
    }

    //We also have a version with a different values for the desired width.
    def thumbnailed(in:Array[Byte]) = resized(in, 120)


  }
}
